/*
 * @Author: rock
 * @Date: 2025-04-19 08:50:35
 * @LastEditors: rock 
 * @LastEditTime: 2025-04-19 09:26:18
 * @FilePath: /006logger/include/logger.h
 * @Description:
 *
 * Copyright (c) 2025 by rock, All Rights Reserved.
 */

#ifndef _LOGGER_H
#define _LOGGER_H


#include <stdexcept>
#include <fstream>
#include <sstream>
#include <atomic>
#include <thread>
#include <chrono>
#include <vector>


#include "logqueue.h"
#include "helper.hpp"

// 日志级别
enum class LogLevel
{
    INFO,
    DEBUG,
    WARN,
    ERROR
};

class Logger
{
public:
    Logger(const std::string &filename)
        : log_file_(filename, std::ios::out | std::ios::app) // 向filename文件中输入输出, 输入输出方式是输出out, 输出方式是追加的方式
          ,
          exit_flag_(false) // 默认退出标记false, 不退出
    {
        if (!log_file_.is_open())
        {
            // 如果文件没有打开, 就抛出异常
            throw std::runtime_error("Failed to open log file\n");
        }
        // 启动工作线程, 开始向log文件中写入msg
        worker_thread_ = std::thread([this]() -> void
                                     {
            std::string msg;
            while(log_queue_.pop(msg))
            {
                log_file_ << msg << std::endl;
            } });
    }

    // 析构善后处理
    ~Logger();

    
    template <typename... Args>
    void log(LogLevel level, const std::string &format, Args &&...args)
    {
        // 格式化log信息
        // 日志级别提示
        std::string str_level;
        switch (level)
        {
        case LogLevel::INFO:
            str_level = "[INFO] ";
            break;
        case LogLevel::DEBUG:
            str_level = "[DEBUG] ";
            break;
        case LogLevel::WARN:
            str_level = "[WARN] ";
            break;
        case LogLevel::ERROR:
            str_level = "[ERROR] ";
            break;
        }
        log_queue_.push(str_level + formatMessage(format, std::forward<Args>(args)...));
    }

private:
    // 获取时间戳
    std::string getCurrentTime()
    {
        auto now = std::chrono::system_clock::now();
        std::time_t time = std::chrono::system_clock::to_time_t(now);
        char buff[100];
        std::strftime(buff, sizeof(buff), "%Y-%m-%d %H:%M-%S", std::localtime(&time));
        return std::string(buff);
    }

    template <typename... Args>
    std::string formatMessage(const std::string &format, Args &&...args)
    {
        std::vector<std::string> arg_strings = {to_string_helper(std::forward<Args>(args))...};
        std::ostringstream oss;
        size_t arg_index = 0;
        size_t pos = 0;

        // 如果format中含有花括号, 把pos位置找到并且把{}替换为字符串
        size_t placeholder = format.find("{}", pos);
        while (placeholder != std::string::npos)
        {
            oss << format.substr(pos, placeholder - pos); // 截取, 获取长度, 写入
            if (arg_index < arg_strings.size())
            {
                oss << arg_strings[arg_index++];
            }
            else
            {
                // arg_strings为空时, 原字符串format不变, 仍然是原来的花括号
                // 这里也可以直接break循环
                oss << "{}";
            }
            // 第一个花括号匹配完

            // 开始匹配第二个花括号
            // 第一个花括号是由三个字符组成
            //  { } ,
            // 所以第二次开始检测的位置应该是pos+2
            pos = placeholder + 2;                // 确定起始查找位置
            placeholder = format.find("{}", pos); // 确定下一次匹配到 { 的位置
        }

        // 剩下的子串如果没有花括号, 或者整个子串如果没有花括号, 把子串原样拼接
        oss << format.substr(pos);
        while (arg_index < arg_strings.size())
        {
            oss << arg_strings[arg_index++];
        }
        return "[" + getCurrentTime() + "] " + oss.str();
    }

    LogQueue log_queue_;          // 操作队列
    std::thread worker_thread_;   // 操作线程
    std::ofstream log_file_;      // 文件输出流, 输出到文件
    std::atomic<bool> exit_flag_; // 退出标记
};

#endif
